﻿namespace Whut.ObjectSharePointMapper.Internals
{
    using System;
    using System.Collections.Generic;
    using Microsoft.SharePoint;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    public class Query<TEntity> : IQuery<TEntity>
    {
        private SPWeb web;

        private MetaDataStore metaDataStore;

        private EntityHydrater entityHydrater;

        private ListInfo listInfo;

        private List<ConditionInfo> conditions = new List<ConditionInfo>();

        public Query(SPWeb web, MetaDataStore metaDataStore, EntityHydrater entityHydrater)
        {
            this.web = web;
            this.metaDataStore = metaDataStore;
            this.entityHydrater = entityHydrater;
            this.listInfo = this.metaDataStore.GetListInfoFor<TEntity>();
        }

        public void AddCondition<TProperty>(Expression<Func<TEntity, TProperty>> propertySelector, ConditionType type, TProperty propertyValue)
        {
            this.AddCondition<TProperty>(ConditionOperator.And, propertySelector, type, propertyValue);
        }

        public void AddCondition<TProperty>(ConditionOperator conditionOperator, Expression<Func<TEntity, TProperty>> propertySelector, ConditionType type, TProperty propertyValue)
        {
            if (propertySelector.Body.NodeType != ExpressionType.MemberAccess)
            {
                throw new ArgumentException("propertySelector", "Expression is not accessing property.");
            }

            MemberExpression memberExpression = (MemberExpression)propertySelector.Body;
            PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
            FieldInfo fieldInfo = this.listInfo.Fields.Single(f => f.PropertyInfo.Name == propertyInfo.Name);
            conditions.Add(new ConditionInfo(conditionOperator, fieldInfo, type, propertyValue));
        }

        // TODO opcja wyczyśc cache na ISession

        public TEntity GetSingleResult()
        {
            ListInfo listInfo = this.metaDataStore.GetListInfoFor<TEntity>();
            SPList list = this.web.GetListByRelativeUrl(listInfo.ListRelativeUrl);
            SPQuery query = this.GetQuery(listInfo);
            query.RowLimit = 1;
            SPListItemCollection items = list.GetItems(query);
            if (items.Count == 0)
            {
                return default(TEntity);
            }

            // TODO?

            return this.entityHydrater.Hydrate<TEntity>(items[0]);
        }

        public IEnumerable<TEntity> GetResults()
        {
            ListInfo listInfo = this.metaDataStore.GetListInfoFor<TEntity>();
            SPList list = this.web.GetListByRelativeUrl(listInfo.ListRelativeUrl);
            SPQuery query = this.GetQuery(listInfo);
            SPListItemCollection items = list.GetItems(query);
            return this.entityHydrater.Hydrate<TEntity>(items);
        }

        private SPQuery GetQuery(ListInfo listInfo)
        {
            SPQuery query = new SPQuery()
            {
                Query = CamlGenerator.GetCamlQueryWhere(conditions),
                // ViewFields = CamlGenerator.GetCamlQueryViewFields(listInfo),
                // IncludeMandatoryColumns = false
            };

            return query;
        }
    }
}
